<?PHP
////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2011 Alexandre Vaillancourt                                  //
//                                                                            //
// Permission is hereby granted, free of charge, to any person obtaining a    //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation  //
// the rights to use, copy, modify, merge, publish, distribute, sublicense,   //
// and/or sell copies of the Software, and to permit persons to whom the      //
// Software is furnished to do so, subject to the following conditions:       //
//                                                                            //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software.                        //
//                                                                            //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        //
// DEALINGS IN THE SOFTWARE.                                                  //
////////////////////////////////////////////////////////////////////////////////

/**
 * Class used to add functions to wikipediaapi
 * 
 * @author Alexandre Vaillancourt <alexandre.vaillancourt@gmail.com>
 * @license http://code.google.com/p/rappelz-hunbot/wiki/License MIT License
 * @copyright Copyright (c) 2012 Alexandre Vaillancourt
 * @since 
 * @package 
 */

require_once("wikibot.classes.php");
 
class wikipediaapi_improved extends wikipediaapi{

	protected $has_logged_in = false;
	protected $has_bot_flag = true;

	/**
	* This is our constructor.
	* @param $in_apiurl string the url of the used mediawiki API, typically http://[domain]/api.php
	* @param $in_has_bot_flag bool whether the bot has the botflag on the used api
	* @return void
	**/
	function __construct ($in_apiurl, $in_has_bot_flag = true) {
		parent::__construct();
		$this->apiurl = $in_apiurl;
		$this->has_logged_in = false;
		$this->has_bot_flag = $in_has_bot_flag;
	}

	/**
	* This function takes a username and password and logs you into wikipedia.
	* @param $user string Username to login as.
	* @param $pass string Password that corresponds to the username.
	* @return bool whether the login was successful or not
	**/
	function login($user, $pass) {
		$this->user = $user;
		$this->pass = $pass;
		$x = unserialize($this->http->post($this->apiurl, array('action' => 'login', 'format' => 'php', 'lgname' => $user, 'lgpassword' => $pass)));
		if($x['login']['result'] == 'Success') {
			$this->has_logged_in = true;
			return true;
		}
		if($x['login']['result'] == 'NeedToken') {
			$x = unserialize($this->http->post($this->apiurl, array('action' => 'login', 'format' => 'php', 'lgname' => $user, 'lgpassword' => $pass, 'lgtoken' => $x['login']['token'])));
			if($x['login']['result'] == 'Success')
				$this->has_logged_in = true;
				return true;
		}
		return false;
	}
	
	
/*
* action=delete *
  Delete a page.

This module requires read rights.
This module requires write rights.
This module only accepts POST requests.
Parameters:
  title	  - Title of the page you want to delete. Cannot be used together with pageid
  pageid	 - Page ID of the page you want to delete. Cannot be used together with title
  token	  - A delete token previously retrieved through prop=info
  reason	 - Reason for the deletion. If not set, an automatically generated reason will be used.
  watch	  - Add the page to your watchlist
  unwatch	- Remove the page from your watchlist
  oldimage       - The name of the old image to delete as provided by iiprop=archivename
Examples:
  api.php?action=delete&title=Main%20Page&token=123ABC
  api.php?action=delete&title=Main%20Page&token=123ABC&reason=Preparing%20for%20move
*/

	function delete($title, $reason) {
		
		$params = Array(
				'action' => 'delete',
				'title' => $title,
				'token' => $this->getedittoken(),
				'reason' => $reason,
				'format' => 'php',
				'bot' => 1,
				'unwatch' => 1
		);

		$x = $this->http->post($this->apiurl,$params);
		$x = unserialize($x);
		// var_export($x);
		if ($x['delete']['title'] == $title) 
			return true;
		if ($x['error']['code'] == 'badtoken') {
			if($this->login($this->user,$this->pass)) {
				$this->gettokens('Main Page',true);
				return $this->delete($title, $reason);
			} else {
				return false;
			}
		}
	}
	
	/**
	 * Returns content of the pages passed as an array of page titles or page ids
	 * @param $page_titles Array(string) List of pages to fetch by their title (not usable with $page_ids)
	 * @param $page_ids Array(string) List of pages to fetch by their page ids (not usable with $page_titles) (Default null)
	 * @param $redirects bool Whether to follow redirects (Default true)
	 * @return Associative array of the pages as Page title => page info with content
	 **/
	function get_pages_content($page_titles, $page_ids = null, $redirects = true) {

		$fetched_pages = array();

		$params = Array(
			'action' => 'query',
			'prop' => 'revisions',
			'rvprop' => 'timestamp|content',
			'format' => 'php',
			'meta' => 'userinfo',
			'token' => $this->getedittoken()
		);
		
		if($this->has_logged_in == false) {
			$item_limit = 50;
		}
		else if($this->has_bot_flag == true) {
			$item_limit = 5000;
			$params['bot'] = '1';
		}
		else {
			$item_limit = 500;
		}
		
		if($redirects == true) {
			$params['redirects'] = '1'; 
		}
		
		$what_to_fetch = 'titles';
		$what_to_fetch_content = $page_titles;

		if($page_ids != null) {
			$what_to_fetch = 'pageids';
			$what_to_fetch_content = $page_ids;
		}
		
		$rounds = ceil(count($what_to_fetch_content)/$item_limit);
		
		for($i = 0; $i < $rounds; ++$i) {
		
			$this_rounds_content = array_slice($what_to_fetch_content, $i * $item_limit, $item_limit);
			$this_rounds_content = implode('|', $this_rounds_content);
			
			$params[$what_to_fetch] = $this_rounds_content;
		
			$x = $this->http->post($this->apiurl, $params);
			$x = unserialize($x);
			
			if(!empty($x['query']['normalized'])) {
				foreach($x['query']['normalized'] as $key => $data) {
					$fetched_pages[$data['from']]['title'] = $data['from'];
					$fetched_pages[$data['from']]['unavailable'] = 'normalized';
					$fetched_pages[$data['from']]['see'] = $data['to'];
				}
			}
			
			if(!empty($x['query']['redirects'])) {
				foreach($x['query']['redirects'] as $key => $data) {
					$fetched_pages[$data['from']]['title'] = $data['from'];
					$fetched_pages[$data['from']]['unavailable'] = 'redirect';
					$fetched_pages[$data['from']]['see'] = $data['to'];
				}
			}
			
			foreach($x['query']['pages'] as $key => $data) {
				$fetched_pages[$data['title']]['title'] = $data['title'];
				if(isset($data['invalid'])) {
					$fetched_pages[$data['title']]['unavailable'] = 'invalid';
				}
				else if(isset($data['missing'])) {
					$fetched_pages[$data['title']]['unavailable'] = 'missing';
				}
				else
				{
					$fetched_pages[$data['title']]['pageid'] = $data['pageid'];
					$fetched_pages[$data['title']]['content_timestamp'] = $data['revisions'][0]['timestamp'];
					$fetched_pages[$data['title']]['content'] = $data['revisions'][0]['*'];
				}
			}
			
			// Prevents server hammering
			if($i + 1 < $rounds) {
				sleep(1);
			}
		}

		return $fetched_pages;
	}
	
        
	function siteinfo($replace_apiurl = null) {
		$url = !empty($replace_apiurl) ? $replace_apiurl : $this->apiurl;
		// $params = array();
		// $params['action'] = 'query';
		// $params['meta'] = 'siteinfo';
		// $params['format'] = 'xml';
		// $params['siprop'] = 'general' . (!empty($statistics)?'|statistics':'');
		$qs = $url.'?action=query&meta=siteinfo&format=php&siprop=general'.(!empty($statistics)?"|statistics":"");
		$x = $this->http->get($qs);
		//$x = $this->http->post($url, $params);
		//print_r($qs);
		//print_r($x);
		$x = unserialize($x);
		return $x['query'];
	}
	
	function get_namespaces() {
		$qs = $this->apiurl.'?format=php&action=query&meta=siteinfo&siprop=namespaces';
		$x = $this->http->get($qs);
		$x = $this->local_unserialize($x);
			
		$namespaces = array();
		if($x !== false) {
			foreach($x['query']['namespaces'] as $namespace_item) {
				if($namespace_item['id'] >= 0) {
					$namespaces[$namespace_item['id']] = array();
					$namespaces[$namespace_item['id']]['id'] = $namespace_item['id'];
					$namespaces[$namespace_item['id']]['name'] = $namespace_item['*'];
				}
			}
		}
		
		return $namespaces;
	}
	
	// response <p pageid="1521" ns="4" title="Rappelz Wiki:Simplified Ruleset" />
	function get_page_list_in_namespace($in_namespace_id) {
	
		$fetched_pages_info = array();
	
		$params = Array(
			'format' => 'php',
			'action' => 'query',
			'list' => 'allpages',
			'apnamespace' => $in_namespace_id
		);
		
		if($this->has_bot_flag == true) {
			$item_limit = 5000;
		}
		else {
			$item_limit = 500;
		}
		
		$params['aplimit'] = $item_limit;
		
		$is_done = false;
		$start_at = null;
		
		while(!$is_done){
			
			if($start_at != null) {
				$params['apfrom'] = $start_at;
			}
			
			$x = $this->http->post($this->apiurl, $params);
			$x = $this->local_unserialize($x);
			
			if($x !== false) {
			
				// response <p pageid="1521" ns="4" title="Rappelz Wiki:Simplified Ruleset" />
				foreach($x['query']['allpages'] as $page_info){
					$fetched_pages_info[] = $page_info;
				}
				
				if(isset($x['query-continue']['allpages']['apfrom'])) {
					$start_at = $x['query-continue']['allpages']['apfrom'];
				}
				else {
					$is_done = true;
				}
			}
		}
		
		return $fetched_pages_info;
	}
	
	function get_page_views($in_array_of_page_ids) {
		
		$fetched_pages_info = array();
		$sucessful_fetched_pages = array();
	
		$params = Array(
			'format' => 'php',
			'action' => 'query',
			'prop' => 'info',
			'inprop' => 'views'
		);

		if($this->has_logged_in == false) {
			$item_limit = 50;
		}
		else {
			$item_limit = 500;
		}
		
		$what_to_fetch_content = $in_array_of_page_ids;
		$rounds = ceil(count($what_to_fetch_content)/$item_limit);
		
		
		
		for($i = 0; $i < $rounds; ++$i) {
		
			$this_rounds_content = array_slice($what_to_fetch_content, $i * $item_limit, $item_limit);
			$this_rounds_content = implode('|', $this_rounds_content);
			
			$params['pageids'] = $this_rounds_content;
		
			$x = $this->http->post($this->apiurl, $params);
			$x = $this->local_unserialize($x);

			if($x !== false) {
				foreach($x['query']['pages'] as $key => $data) {
					$sucessful_fetched_pages[] = $key;
					
					$fetched_pages_info[$data['pageid']] = array();
					$fetched_pages_info[$data['pageid']]['pageid'] = $data['pageid'];
					if(isset($data['invalid'])) {
						$fetched_pages_info[$data['pageid']]['unavailable'] = 'invalid';
					}
					else if(isset($data['missing'])) {
						$fetched_pages_info[$data['pageid']]['unavailable'] = 'missing';
					}
					else
					{
						$fetched_pages_info[$data['pageid']]['views'] = $data['views'];
						//$fetched_pages_info[$data['pageid']]['touched'] = $data['touched'];
						//$fetched_pages_info[$data['pageid']]['lastrevid'] = $data['lastrevid'];
						//$fetched_pages_info[$data['pageid']]['length'] = $data['length'];
					}
				}
			}
			
			// Prevents server hammering
			//if($i + 1 < $rounds) {
			//	sleep(1);
			//}
			
		}
		
		$pages_not_done = array_diff($in_array_of_page_ids, $sucessful_fetched_pages);
		
		if(count($pages_not_done) > 0) {
			die('Array not all done');
		}
		
		return $fetched_pages_info;
	}
	
	/* 
action=upload *
  Upload a file, or get the status of pending uploads. Several methods are available:
   * Upload file contents directly, using the "file" parameter
   * Have the MediaWiki server fetch a file from a URL, using the "url" parameter
   * Complete an earlier upload that failed due to warnings, using the "sessionkey" parameter
  Note that the HTTP POST must be done as a file upload (i.e. using multipart/form-data) when
  sending the "file". Note also that queries using session keys must be
  done in the same login session as the query that originally returned the key (i.e. do not
  log out and then log back in). Also you must get and send an edit token before doing any upload stuff.

This module requires read rights.
This module requires write rights.
This module only accepts POST requests.
Parameters:
  filename       - Target filename
  comment	- Upload comment. Also used as the initial page text for new files if "text" is not specified
		   Default: 
  text	   - Initial page text for new files
  token	  - Edit token. You can get one of these through prop=info
  watch	  - Watch the page
  ignorewarnings - Ignore any warnings
  file	   - File contents
  url	    - Url to fetch the file from
  sessionkey     - Session key returned by a previous upload that failed due to warnings
Examples:
  Upload from a URL:
      api.php?action=upload&filename=Wiki.png&url=http%3A//upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png
  Complete an upload that failed due to warnings:
      api.php?action=upload&filename=Wiki.png&sessionkey=sessionkey&ignorewarnings=1
*/	
	function upload($filename, $filepath, $url, $comment, $mimetype, $forceoverwrite = false, $ignorewarnings = false) {
		$params = Array(
				'action' => 'upload',
				'filename' => $filename,
				'comment' => $comment,
				'token' => $this->getedittoken(),
				'bot' => 1,
				'format' => 'php'
		);
		
		if($ignorewarnings === true) {
			$params['ignorewarnings'] = 1;
		}
		
		if(!empty($filepath) && file_exists($filepath) && is_file($filepath)) {
			$params['file'] = '@'.$filepath.';type='.$mimetype;
		}
		else if(!empty($url)) {
			$params['url'] = $url;
		}
		else
		{
			return false;
		}
		
		//print "Uploading:\n";
		//print_r($params);

		$x = $this->http->post($this->apiurl,$params);
		
		//print_r($x);
		
		$x = unserialize($x);
		//var_export($x); print "\n";
		if ($x['upload']['result'] == 'Success') 
			return true;
		if ($x['upload']['result'] == 'Warning' 
			&& isset($x['upload']['warnings']['exists'])
			&& $forceoverwrite === true) {
			return $this->upload($filename, $filepath, $url, $comment, $mimetype, $forceoverwrite, true);
		}
		if ($x['error']['code'] == 'badtoken') {
			if($this->login($this->user,$this->pass)) {
				$this->gettokens('Main Page',true);
				return $this->upload($filename, $filepath, $url, $comment, $mimetype, $forceoverwrite, $ignorewarnings);
			} 
		}
		
		var_export($params);
		var_export($x);
		
		return false;
	}
	
	function local_unserialize($in_data){
		$return_data = unserialize($in_data);
		if($return_data === false) {
			global $logfd; if (!is_resource($logfd)) $logfd = fopen('php://stderr','w'); fwrite($logfd,'UNSERIALIZE ERROR; DATA: ' . $in_data . "\n");
			return false;
		}
		return $return_data;
	}
	
	
}


?>
